package com.forum.core.dao.hibernate;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.metadata.ClassMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.forum.core.util.ClassUtil;
import com.forum.core.util.ReflectionUtils;

/**
 * @version <1.0>
 * @Company <Jinher Technology Development Company LTD.>
 * @Project <Jinher Collaboration Suite>
 * @Author <yangzq>
 * @Date <May 18, 2009>
 * @description 封装Hibernate原生API的CRUD泛型基类.
 * <p/>
 * 可在Service层直接使用,也可以扩展泛型DAO子类使用.
 * 参考Spring2.5自带的Petlinc例子,取消了HibernateTemplate,直接使用Hibernate原生API.
 */
@SuppressWarnings("unchecked")
public class SimpleHibernateDao {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    protected SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    /**
     * 保存新增对象.
     */
    public void saveOrUpdate(final Object entity) {
        Assert.notNull(entity, "entity不能为空");
        ClassMetadata metadata = sessionFactory.getClassMetadata(entity.getClass());
        String pk = metadata.getIdentifierPropertyName();
        Object obj = ClassUtil.getFieldValue(entity, pk);
        if (obj == null) {
            save(entity);
        } else {
            update(entity);
        }
    }

    /**
     * 保存新增对象.
     */
    public void save(final Object entity) {
        Assert.notNull(entity, "entity不能为空");
        getSession().save(entity);
        logger.debug("save entity: {}", entity);
    }

    /**
     * 修改的对象.
     */
    public void update(final Object entity) {
        Assert.notNull(entity, "entity不能为空");
        getSession().update(entity);
        logger.debug("save entity: {}", entity);
    }

    /**
     * 删除对象.
     *
     * @param entity 对象必须是session中的对象或含id属性的transient对象.
     */
    public void delete(final Object entity) {
        Assert.notNull(entity, "entity不能为空");
        getSession().delete(entity);
        logger.debug("delete entity: {}", entity);
    }

    /**
     * 按id删除对象.
     */
    public void delete(Class clazz, final Serializable id) {
        Assert.notNull(id, "id不能为空");
        Assert.notNull(clazz, "clazz不能为空");
        getSession().delete(load(clazz, id));
    }

    /**
     * 按id获取对象.get方法
     */
    public <T> T get(Class<T> clazz, final Serializable id) {
        Assert.notNull(id, "id不能为空");
        Assert.notNull(clazz, "clazz不能为空");
        return (T) this.getSession().get(clazz, id);
    }

    /**
     * 根据ID获取对象load方法
     *
     * @param id
     * @return
     */
    public <T> T load(Class<T> clazz, final Serializable id) {
        Assert.notNull(id, "id不能为空");
        Assert.notNull(clazz, "clazz不能为空");
        return (T) getSession().load(clazz, id);
    }

    /**
     * 获取全部对象.
     */
    public List getAll(Class clazz) {
        Assert.notNull(clazz, "clazz不能为空");
        return find("from " + clazz.getName());
    }

    /**
     * 按HQL查询对象列表.
     *
     * @param values 数量可变的参数,按顺序绑定.
     */
    public List find(final String hql, final Object... values) {
        return createQuery(hql, values).list();
    }

    public List find(final String hql, final Object value) {
        if (value != null) {
            return createQuery(hql, new Object[]{value}).list();
        }
        return createQuery(hql).list();
    }

    /**
     * 按HQL查询对象列表.
     *
     * @param values 命名参数,按名称绑定.
     */
    public List find(final String hql, final Map<String, Object> values) {
        return createQuery(hql, values).list();
    }

    /**
     * 按HQL查询唯一对象.
     *
     * @param values 数量可变的参数,按顺序绑定.
     */
    public <T> T findUnique(final String hql, final Object... values) {
        return (T) createQuery(hql, values).uniqueResult();
    }

    /**
     * 按HQL查询唯一对象.
     *
     * @param values 命名参数,按名称绑定.
     */
    public <T> T findUnique(final String hql, final Map<String, Object> values) {
        return (T) createQuery(hql, values).uniqueResult();
    }

    /**
     * 执行HQL进行批量修改/删除操作.
     */
    public int batchExecute(final String hql, final Object... values) {
        return createQuery(hql, values).executeUpdate();
    }

    /**
     * 执行HQL进行批量修改/删除操作.
     *
     * @return 更新记录数.
     */
    public int batchExecute(final String hql, final Map<String, ?> values) {
        return createQuery(hql, values).executeUpdate();
    }

    /**
     * 根据查询HQL与参数列表创建Query对象.
     * <p/>
     * 本类封装的find()函数全部默认返回对象类型为T,当不为T时使用本函数.
     *
     * @param values 数量可变的参数,按顺序绑定.
     */
    public Query createQuery(final String queryString, final Object... values) {
        Assert.hasText(queryString, "queryString不能为空");
        Query query = getSession().createQuery(queryString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query;
    }

    /**
     * 根据查询HQL与参数列表创建Query对象.
     *
     * @param values 命名参数,按名称绑定.
     */
    public Query createQuery(final String queryString, final Map<String, ?> values) {
        Assert.hasText(queryString, "queryString不能为空");
        Query query = getSession().createQuery(queryString);
        if (values != null) {
            query.setProperties(values);
        }
        return query;
    }

    /**
     * 根据查询SQL与参数列表创建Query对象.
     * <p/>
     * 本类封装的find()函数全部默认返回对象类型为T,当不为T时使用本函数.
     *
     * @param values 数量可变的参数,按顺序绑定.
     */
    public Query createSQLQuery(final String queryString, final Object... values) {
        Assert.hasText(queryString, "queryString不能为空");
        Query query = getSession().createSQLQuery(queryString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query;
    }

    /**
     * 根据查询SQL与参数列表创建Query对象.
     *
     * @param values 命名参数,按名称绑定.
     */
    public Query createSQLQuery(final String queryString, final Map<String, Object> values) {
        Assert.hasText(queryString, "queryString不能为空");
        Query query = getSession().createSQLQuery(queryString);
        if (values != null) {
            query.setProperties(values);
        }
        return query;
    }

    /**
     * 初始化对象.
     * 使用load()方法得到的仅是对象Proxy后, 在传到View层前需要进行初始化.
     * initObject(user) ,初始化User的直接属性，但不会初始化延迟加载的关联集合和属性.
     * initObject(user.getRoles())，初始化User的直接属性和关联集合.
     * initObject(user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
     */
    public void initObject(Object object) {
        Hibernate.initialize(object);
    }

    /**
     * 批量初始化对象.
     *
     * @see #initObject(Object)
     */
    public void initEntity(List entityList) {
        for (Object entity : entityList) {
            Hibernate.initialize(entity);
        }
    }

    /**
     * 通过Set将不唯一的对象列表唯一化.
     * 主要用于HQL/Criteria预加载关联集合形成重复记录,又不方便使用distinct查询语句时.
     */
    public List distinct(List list) {
        Set set = new LinkedHashSet(list);
        return new ArrayList(set);
    }

    public Query distinct(Query query) {
        query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        return query;
    }

    public Criteria distinct(Criteria c) {
        c.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        return c;

    }

    /**
     * 取得对象的主键名.
     */
    public String getIdName(Class entityClass) {
        ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
        Assert.notNull(meta, "Class " + entityClass.getSimpleName() + " not define in HibernateSessionFactory.");
        return meta.getIdentifierPropertyName();
    }

    /**
     * 执行hql语句。
     */
    public void executeHQL(String hql) {
        getSession().createQuery(hql).executeUpdate();

    }

    public void executeHQL(String hql, final Object... values) {
        if (values == null || values.length == 0) {
            executeHQL(hql);
        }
        Query query = getSession().createQuery(hql);
        for (int i = 0; i < values.length; i++) {
            query.setParameter(i, values[i]);
        }
        query.executeUpdate();
    }

    /**
     * 执行hql语句。
     */
    public void executeDelete(String hql) {
        getSession().delete(hql, null);
    }


    /**
     * 执行sql语句。
     *
     * @throws SQLException
     * @throws HibernateException
     */
    @SuppressWarnings("deprecation")
    public void executeSQL(String sql) {
        org.hibernate.Transaction tx = null;
        Session session = null;
        try {
            session = this.getSessionFactory().getCurrentSession();
            tx = session.beginTransaction();
            session.createSQLQuery(sql).executeUpdate();
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (session != null)
                    session.close();
            } catch (HibernateException e1) {
                e1.printStackTrace();
            }

        }
    }

    /**
     * 效验数据库是否存在该记录
     */
    public boolean isTrueHQL(String hql) {

        boolean flag = false;
        Query query = getSession().createQuery(hql);
        List vlist = query.list();

        if (vlist != null && vlist.size() != 0) {
            flag = true;
        }

        return flag;
    }


    /**
     * @param entity
     * @param fields
     * @author zhangjj
     * @Date 2010-08-25 10:19:24
     * @description 有时候我们只想更新表的部分字段，可是hibernate不支持部分字段的更新，所以这里写了一个这样的方法，根据主键更新部分字段，多个字段通过“,”分割。
     */
    public void update(final Object entity, String fields) {
        Assert.notNull(fields, "id不能为空");
        Assert.notNull(entity, "entity不能为空");
        String idName = getIdName(entity.getClass());
        String id = (String) ReflectionUtils.getFieldValue(entity, idName);
        Object entitytemp = getSession().get(entity.getClass(), id);
        String[] fieldArray = fields.split(",");
        for (String field : fieldArray) {
            Object value = ReflectionUtils.getFieldValue(entity, field);
            ReflectionUtils.setFieldValue(entitytemp, field, value);
        }
    }

}